﻿using System.IO;
using System.Text;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using LFSRecord2.Misc;
using System;
using System.Diagnostics;

namespace LFSRecord2.View.ThreeD
{
    public class LfsTrackMesh 
    {
        public string FileName { get; set; }
        public string TrackName { get; private set; }

        public Color GroundCol { get; private set; }

        public int NumObjects { get; private set; }
        public LfsMeshObject[] TrackMeshObjects;

        public int NumCPObjects { get; private set; }
        public int[] CPObIndeces;

        public LfsTrackMesh()
        {
        }

        public LfsTrackMesh(string fileName)
        {
            FileName = fileName;
        }

        public bool Load()
        {
            // Open the file
            FileStream fs;
            BinaryReader bfs;
            try
            {
                //Debug.WriteLine("loading file '" + FileName + "'");
                fs = File.OpenRead(FileName);
                bfs = new BinaryReader(fs, Encoding.ASCII);
            }
            catch (ArgumentException)
            {
                return false;
            }
            catch (IOException)
            {
                return false;
            }
            catch (NotSupportedException)
            {
                return false;
            }
            catch (UnauthorizedAccessException)
            {
                return false;
            }

            if (fs.Length < 4096)
            {
                bfs.Close();
                fs.Close();
                return false;
            }

            // Read header
            string LFSSMX = new string(bfs.ReadChars(6));
            if (LFSSMX != "LFSSMX")
            {
                bfs.Close();
                fs.Close();
                return false;
            }

            fs.Position = 16;
            TrackName = FileHelper.CharArrayToString(bfs.ReadChars(32));
            //Debug.WriteLine("Track name : " + _trackName);

            Color col = new Color();
            col.R = bfs.ReadByte();
            col.G = bfs.ReadByte();
            col.B = bfs.ReadByte();
            GroundCol = col;

            fs.Position = 60;

            NumObjects = bfs.ReadInt32();

            TrackMeshObjects = new LfsMeshObject[NumObjects];

            // Read objects
            Point3D objectCenter;
            int radius, numPoints, numTris;
            //Int32 triIndex;
            int numTriIndices;
            int c;
            for (int a = 0; a < NumObjects; a++)
            {
                // Check if there's enough data available for this block
                if (fs.Position + 24 > fs.Length)
                {
                    bfs.Close();
                    fs.Close();
                    return false;
                }

                // Object data
                objectCenter = new Point3D();
                objectCenter.X = bfs.ReadInt32();
                objectCenter.Y = bfs.ReadInt32();
                objectCenter.Z = bfs.ReadInt32();

                radius = bfs.ReadInt32();
                numPoints = bfs.ReadInt32();
                numTris = bfs.ReadInt32() * 3;

                TrackMeshObjects[a] = new LfsMeshObject(objectCenter, radius, numPoints, numTris);

                // Object points
                for (int b = 0; b < numPoints; b++)
                {
                    // Check if there's enough data available for this block
                    if (fs.Position + 16 > fs.Length)
                    {
                        bfs.Close();
                        fs.Close();
                        return false;
                    }

                    TrackMeshObjects[a].Points[b] = new Point3D(bfs.ReadInt32() / 65536.0d, bfs.ReadInt32() / 65536.0d, bfs.ReadInt32() / 65536.0d);

                    // object colour from final vertex colour
                    col = new Color();
                    col.B = bfs.ReadByte();
                    col.G = bfs.ReadByte();
                    col.R = bfs.ReadByte();
                    col.A = bfs.ReadByte();
                    TrackMeshObjects[a].setObjectColor(col);
                }

                // Object triangles
                c = 0;
                numTriIndices = numTris / 3;
                for (int b = 0; b < numTriIndices; b++)
                {
                    // Check if there's enough data available for this block
                    if (fs.Position + 8 > fs.Length)
                    {
                        bfs.Close();
                        fs.Close();
                        return false;
                    }

                    TrackMeshObjects[a].Tris[c++] = bfs.ReadUInt16();
                    TrackMeshObjects[a].Tris[c++] = bfs.ReadUInt16();
                    TrackMeshObjects[a].Tris[c++] = bfs.ReadUInt16();

                    // Skip spare byte
                    bfs.ReadUInt16();
                }
            }

            // Read footer
            // Check if there's enough data available for this block
            if (fs.Position + 4 > fs.Length)
            {
                bfs.Close();
                fs.Close();
                return false;
            }
            NumCPObjects = bfs.ReadInt32();

            CPObIndeces = new int[NumCPObjects];
            for (int b = 0; b < NumCPObjects; b++)
            {
                // Check if there's enough data available for this block
                if (fs.Position + 4 > fs.Length)
                {
                    bfs.Close();
                    fs.Close();
                    return false;
                }
                CPObIndeces[b] = bfs.ReadInt32();
            }

            bfs.Close();
            fs.Close();

            return true;
        }

        public void UnloadMeshData()
        {
            TrackMeshObjects = null;
        }
    }
}
